﻿//-----------------------------------------------------------------------------
// <copyright file="ViewTests.cs" company="William E. Kempf">
//     Copyright (c) William E. Kempf.
// </copyright>
//-----------------------------------------------------------------------------

namespace OnyxTests
{
    using System;
    using System.Linq;
    using System.Windows;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using Onyx.Windows;
    using OnyxTests.TestArtifacts;
    using Testing.Specificity;

    /// <summary>
    /// Provides tests for the <see cref="View"/> class.
    /// </summary>
    [TestClass]
    public class ViewTests
    {
        /// <summary>
        /// Verifies that <see cref="View.GetView"/> should throw <see cref="ArgumentNullException"/>
        /// when given a null reference.
        /// </summary>
        [TestMethod]
        public void GetView_GivenNull_ShouldThrow()
        {
            Specify.ThatAction(() =>
                {
                    View.GetView(null);
                }).ShouldHaveThrown(typeof(ArgumentNullException));
        }

        /// <summary>
        /// Verifies that <see cref="View.GetView"/> should return a <see cref="View"/> instance
        /// when given a valid reference to a <see cref="DependencyObject"/>.
        /// </summary>
        [TestMethod]
        public void GetView_GivenInstance_ShouldReturnViewInstance()
        {
            DependencyObject element = new DependencyObject();
            View view = View.GetView(element);
            Specify.That(view).ShouldNotBeNull();
        }

        /// <summary>
        /// Verifies that <see cref="View.GetView"/> creates an instance of <see cref="View"/> with
        /// <see cref="P:View.ViewElement"/> set to the source element.
        /// </summary>
        [TestMethod]
        public void GetView_GivenInstance_ShouldSetViewElement()
        {
            DependencyObject element = new DependencyObject();
            View view = View.GetView(element);
            Specify.That(view.ViewElement).ShouldBeSameAs(element);
        }

        /// <summary>
        /// Verifies the <see cref="E:View.ViewCreated"/> event is raised when a <see cref="View"/> is created.
        /// </summary>
        [TestMethod]
        public void GetView_GivenInstance_ShouldRaiseCreatedEvent()
        {
            EventWatcher<ViewCreatedEventArgs> watcher = new EventWatcher<ViewCreatedEventArgs>();
            View.ViewCreated += watcher.EventHandler;
            DependencyObject element = new DependencyObject();
            View.GetView(element);
            Specify.That(watcher.First().ViewElement).ShouldBeSameAs(element);
        }

        /// <summary>
        /// Verifies that the <see cref="E:View.ViewCreated"/> event raises an event that
        /// allows a service to be associated with the <see cref="View"/>.
        /// </summary>
        [TestMethod]
        public void ViewCreated_GivenHandler_ShouldProvideServiceContainer()
        {
            View.ViewCreated += (s, e) =>
                {
                    e.ServiceContainer.RegisterService(typeof(IServiceProvider), e.ServiceContainer);
                };
            DependencyObject element = new DependencyObject();
            View view = View.GetView(element);
            IServiceProvider serviceProvider = (IServiceProvider)view.GetService(typeof(IServiceProvider));
            Specify.That(serviceProvider).ShouldNotBeNull();
        }

        /// <summary>
        /// Verifies that <see cref="View.GetModel"/> given <see langword="null"/> should throw
        /// <see cref="ArgumentNullException"/>.
        /// </summary>
        [TestMethod]
        public void GetModel_GivenNull_ShouldThrow()
        {
            Specify.ThatAction(() =>
                {
                    View.GetModel(null);
                }).ShouldHaveThrown(typeof(ArgumentNullException));
        }

        /// <summary>
        /// Verifies that <see cref="View.SetModel"/> given <see langword="null"/> for the source
        /// should throw <see cref="ArgumentNullException"/>.
        /// </summary>
        [TestMethod]
        public void SetModel_GivenNullSource_ShouldThrow()
        {
            Specify.ThatAction(() =>
                {
                    View.SetModel(null, new object());
                }).ShouldHaveThrown(typeof(ArgumentNullException));
        }

        /// <summary>
        /// Verifies that <see cref="View.SetModel"/> should set the value of the attached property.
        /// </summary>
        [TestMethod]
        public void SetModel_GivenModel_ShouldSetValue()
        {
            DependencyObject element = new DependencyObject();
            View view = View.GetView(element);
            ViewModelCreatedWithView model = new ViewModelCreatedWithView(view);
            View.SetModel(element, model);
            Specify.That(View.GetModel(element)).ShouldBeSameAs(model);
        }

        /// <summary>
        /// Verifies that <see cref="View.SetModel"/> will set the <see cref="FrameworkElement.DataContext"/>
        /// dependency property.
        /// </summary>
        [TestMethod]
        public void SetModel_OnFrameworkElementGivenModel_ShouldSetDataContext()
        {
            FrameworkElement element = new FrameworkElement();
            View view = View.GetView(element);
            ViewModelCreatedWithView model = new ViewModelCreatedWithView(view);
            View.SetModel(element, model);
            Specify.That(element.DataContext).ShouldBeSameAs(model);
        }

        /// <summary>
        /// Verifies that <see cref="View.SetModel"/> given a <see cref="Type"/> should construct
        /// an instance of that type, passing the <see cref="View"/> to the constructor, and assign
        /// that instance to the "Model" attached property.
        /// </summary>
        [TestMethod]
        public void SetModel_GivenType_ShouldConstructModelWithView()
        {
            DependencyObject element = new DependencyObject();
            View view = View.GetView(element);
            View.SetModel(element, typeof(ViewModelCreatedWithView));
            ViewModelCreatedWithView model = View.GetModel(element) as ViewModelCreatedWithView;
            Specify.That(model.View).ShouldBeSameAs(view);
        }

        /// <summary>
        /// Verifies that <see cref="View.SetModel"/> given a <see cref="Type"/> should construct
        /// an instance of that type, passing the "view element" to the constructor, and assign
        /// that instance to the "Model" attached property.
        /// </summary>
        [TestMethod]
        public void SetModel_GivenType_ShouldConstructModelWithViewElement()
        {
            DependencyObject element = new DependencyObject();
            View.GetView(element);
            View.SetModel(element, typeof(ViewModelCreatedWithViewElement));
            ViewModelCreatedWithViewElement model = View.GetModel(element) as ViewModelCreatedWithViewElement;
            Specify.That(model.ViewElement).ShouldBeSameAs(element);
        }

        /// <summary>
        /// Verifies that <see cref="View.SetModel"/> given a <see cref="Type"/> should construct
        /// an instance of that type using the default constructor, and assign
        /// that instance to the "Model" attached property.
        /// </summary>
        [TestMethod]
        public void SetModel_GivenType_ShouldConstructModelWithDefaultConstructor()
        {
            DependencyObject element = new DependencyObject();
            View.GetView(element);
            View.SetModel(element, typeof(ViewModelConstructedWithNothing));
            ViewModelConstructedWithNothing model = View.GetModel(element) as ViewModelConstructedWithNothing;
            Specify.That(model).ShouldNotBeNull();
        }

        /// <summary>
        /// Verifies taht <see cref="View.GetService"/> given <see langword="null"/> should throw
        /// <see cref="ArgumentNullException"/>.
        /// </summary>
        [TestMethod]
        public void GetService_GivenNullType_ShouldThrow()
        {
            DependencyObject element = new DependencyObject();
            View view = View.GetView(element);
            Specify.ThatAction(() =>
                {
                    view.GetService(null);
                }).ShouldHaveThrown(typeof(ArgumentNullException));
        }

        /// <summary>
        /// Verifies that <see cref="View"/> instances for <see cref="UIElement"/> types have a
        /// <see cref="ICommandTarget"/> service.
        /// </summary>
        [TestMethod]
        public void GetService_ForUIElementGivenICommandTarget_ShouldReturnInstance()
        {
            UIElement element = new UIElement();
            View view = View.GetView(element);
            ICommandTarget service = view.GetService(typeof(ICommandTarget)) as ICommandTarget;
            Specify.That(service).ShouldNotBeNull();
        }

        /// <summary>
        /// Verifies that <see cref="View"/> instances for <see cref="UIElement"/> types have a
        /// <see cref="IFocusSuggested"/> service.
        /// </summary>
        [TestMethod]
        public void GetService_ForUIElementGivenIFocusSuggested_ShouldReturnInstance()
        {
            UIElement element = new UIElement();
            View view = View.GetView(element);
            IFocusSuggested service = view.GetService(typeof(IFocusSuggested)) as IFocusSuggested;
            Specify.That(service).ShouldNotBeNull();
        }

        /// <summary>
        /// Verifies that <see cref="View"/> instances for <see cref="UIElement"/> types have a
        /// <see cref="IDisplayMessage"/> service.
        /// </summary>
        [TestMethod]
        public void GetService_ForUIElementGivenIDisplayMessage_ShouldReturnInstance()
        {
            UIElement element = new UIElement();
            View view = View.GetView(element);
            IDisplayMessage service = view.GetService(typeof(IDisplayMessage)) as IDisplayMessage;
            Specify.That(service).ShouldNotBeNull();
        }

        /// <summary>
        /// Verifies that <see cref="View"/> instances for <see cref="UIElement"/> types have a
        /// <see cref="ICommonDialogProvider"/> service.
        /// </summary>
        [TestMethod]
        public void GetService_ForUIElementGivenICommonDialogProvider_ShouldReturnInstance()
        {
            UIElement element = new UIElement();
            View view = View.GetView(element);
            ICommonDialogProvider service = view.GetService(typeof(ICommonDialogProvider)) as ICommonDialogProvider;
            Specify.That(service).ShouldNotBeNull();
        }

        /// <summary>
        /// Verifies that <see cref="View"/> instances for <see cref="FrameworkElement"/> types have a
        /// <see cref="IElementLifetime"/> service.
        /// </summary>
        [TestMethod]
        public void GetService_ForFrameworkElementGivenIElementLifetime_ShouldReturnInstance()
        {
            FrameworkElement element = new FrameworkElement();
            View view = View.GetView(element);
            IElementLifetime service = view.GetService(typeof(IElementLifetime)) as IElementLifetime;
            Specify.That(service).ShouldNotBeNull();
        }

        /// <summary>
        /// Verifies that <see cref="View"/> instances for <see cref="FrameworkContentElement"/> types have a
        /// <see cref="IElementLifetime"/> service.
        /// </summary>
        [TestMethod]
        public void GetService_ForFrameworkContentElementGivenIElementLifetime_ShouldReturnInstance()
        {
            FrameworkContentElement element = new FrameworkContentElement();
            View view = View.GetView(element);
            IElementLifetime service = view.GetService(typeof(IElementLifetime)) as IElementLifetime;
            Specify.That(service).ShouldNotBeNull();
        }

        /// <summary>
        /// Verifies <see cref="View.GetService"/> will return the <see cref="View.ViewElement"/> if it
        /// implements the interface.
        /// </summary>
        [TestMethod]
        public void GetService_GivenInterfaceTypeImplementedByViewElement_ShouldReturnViewElement()
        {
            TestViewElement element = new TestViewElement();
            View view = View.GetView(element);
            IFocusSuggested service = view.GetService(typeof(IFocusSuggested)) as IFocusSuggested;
            Specify.That(service).ShouldBeSameAs(element);
        }
    }
}
